package servlets;

import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashSet;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import model.Course;
import model.InvalidSessionException;
import model.ResultObject;
import model.Session;
import model.User;

import database.CourseExistsException;
import database.DBHandler;

public class Courses extends AServlet {
	private static final long serialVersionUID = 1L;
	private static final int MAXGROUPS = 3; 
	
	public void doGet(HttpServletRequest request, HttpServletResponse response)
		throws ServletException, IOException {
		HttpSession session = request.getSession();
		if (session == null || session.getAttribute("LoginStatus") == null
				|| !(Boolean) session.getAttribute("LoginStatus")) {
			response.sendRedirect("index.jsp");
			return;
		}

		if (request.getParameter("get") != null) {
			if (request.getParameter("get").equals("addCourse")) {
				RequestDispatcher rd = getServletConfig().getServletContext()
						.getRequestDispatcher(
								"/WEB-INF/JSP-Files/addCourse.jsp");
				rd.forward(request, response);
				return;
			}
		}
		
		if (request.getParameter("registerTarget") != null){
			try {
				doRegisterToCourse(request, response);
				return;
			} catch (Exception e) {
				e.printStackTrace();
				throw new ServletException();
			} 
		}
		
		if (request.getParameter("unRegisterTarget") != null){
			try {
				doUnRegisterFromCourse(request, response);
				return;
			} catch (Exception e) {
				e.printStackTrace();
				throw new ServletException();
			} 
		}
		
		if (request.getParameter("deleteTarget") != null){
			try {
				doRemoveCourse(request, response);
				return;
			} catch (Exception e) {
				e.printStackTrace();
				throw new ServletException();
			} 
		}
		
		Connection con = DBHandler.getHandler().getLocalCon();
		try {
			ArrayList<Course> filteredCourses = new ArrayList<Course>();
			ArrayList<Course> allCourses = DBHandler.getHandler().getAllCourses(con);
			String userName = (String) session.getAttribute("LoginName");
			if (request.getParameter("showAvailable") != null && request.getParameter("showAvailable").equals("true")){
				request.getSession().setAttribute("coursesFilter", true);
				for(Course c : allCourses){
					if(canRegister(userName, c.getCourseName(), con).getResult()){
						filteredCourses.add(c);
					}
				}
				request.getSession().setAttribute("coursesData", filteredCourses);
			}
			else{
				request.getSession().setAttribute("coursesFilter", false);
				request.getSession().setAttribute("coursesData", allCourses);
			}
		} catch (Exception e) {
			e.printStackTrace();
			finishTransaction(con, false);
			DBHandler.getHandler().returnLocalCon(con);
		}
		
		finishTransaction(con, false);
		DBHandler.getHandler().returnLocalCon(con);
		RequestDispatcher rd = getServletConfig().getServletContext()
				.getRequestDispatcher("/WEB-INF/JSP-Files/courses.jsp");
		rd.forward(request, response);
		return;
	}

	public void doAddNewCourse(HttpServletRequest request, HttpServletResponse response) throws ServletException{
		if(request.getParameter("courseName").equals("") || request.getParameter("description").equals("") || 
				request.getParameter("numOfpts").equals("") || request.getParameter("capacity").equals("") || 
				request.getParameter("groupNum").equals("")){
			forwardOnError(request, response, "NewCourseError", "You must fill in all the fields!", "/WEB-INF/JSP-Files/addCourse.jsp");
			return;
		}
		
		String courseName = request.getParameter("courseName");
		String description = request.getParameter("description");		
		int numOfpts = -1;
		int capacity = -1;
		int groupNum = -1;
			
		try{
			numOfpts = Integer.parseInt(request.getParameter("numOfpts"));
			capacity = Integer.parseInt(request.getParameter("capacity"));
			groupNum = Integer.parseInt(request.getParameter("groupNum"));
			if (numOfpts <= 0 || capacity <= 0 || groupNum <= 0)
				throw new NullPointerException();
		}catch(NumberFormatException e){
			forwardOnError(request, response, "NewCourseError", "Number fields are invalid!", "/WEB-INF/JSP-Files/addCourse.jsp");
			return;
		}
		String[] sessionDay = request.getParameterValues("day");
		String[] sessionStart = request.getParameterValues("start");
		String[] sessionDuration = request.getParameterValues("duration");
		ArrayList<Session> sessions = new ArrayList<Session>();
		DBHandler dbh = DBHandler.getHandler();
		Connection con = dbh.getLocalCon();
		Session session = null;
		
		//checking params 
		try{
			for(int i = 0; i < sessionDay.length; i++){
				 session = new Session(Integer.parseInt(sessionDay[i]),
						 Integer.parseInt(sessionStart[i]),
						 Integer.parseInt(sessionStart[i])+ Integer.parseInt(sessionDuration[i]));
				 sessions.add(session); 
			}
		} catch (InvalidSessionException e){
			finishTransaction(con, false);
			dbh.returnLocalCon(con);
			forwardOnError(request, response, "NewCourseError", "Session time exceeds study hours.", "/WEB-INF/JSP-Files/addCourse.jsp");
			return;
		}

		//checking overlapping sessions in the new course
		boolean overlap = false;
		for (Session newSession:sessions){
			if(newSession.checkOverlapList(sessions)) 
				overlap = true;
		}
		
		if(overlap){
			forwardOnError(request, response, "NewCourseError", "The course sessions overlap.", "/WEB-INF/JSP-Files/addCourse.jsp");
			finishTransaction(con, false);
			dbh.returnLocalCon(con);
			return;
		}
		Course newCourse = new Course(courseName,description,numOfpts,capacity,0,groupNum,sessions,
				(String)request.getSession().getAttribute("LoginName"),new ArrayList<String>());

		//checking overlapping sessions in the same group in Database
		ArrayList<Course> courses = null;
		try {
			courses = dbh.getCoursesByGroup(groupNum, con);
		} catch (Exception e1) {
			finishTransaction(con, false);
			e1.printStackTrace();
			throw new ServletException();
		}
		
		for (Course existingCourse:courses){
			if(existingCourse.checkOverlap(newCourse)){
				finishTransaction(con, false);
				dbh.returnLocalCon(con);
				forwardOnError(request, response, "NewCourseError", "The course sessions overlap with existing courses.", "/WEB-INF/JSP-Files/addCourse.jsp");
				return;
			}
		}

		boolean result = false;
		try{
			result = dbh.addCourse(newCourse, con);
		} catch (CourseExistsException e){
			finishTransaction(con, false);
			dbh.returnLocalCon(con);
			forwardOnError(request, response, "NewCourseError", "Course already exists!", "/WEB-INF/JSP-Files/addCourse.jsp");
			return;
		}
		
		if (!result){
			finishTransaction(con, false);
			dbh.returnLocalCon(con);
			try {
				response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
			} catch (IOException e) {
			}
			return;
		}

		finishTransaction(con, true);
		dbh.returnLocalCon(con);
		request.getSession().setAttribute("PopupSuccessMessage", "Course added successfully");
		RequestDispatcher rd = getServletConfig().getServletContext()
				.getRequestDispatcher("/WEB-INF/JSP-Files/success.jsp");
		try {
			rd.forward(request, response);
		} catch (IOException e) {
		}
	}
	
	public void doRemoveCourse(HttpServletRequest request, HttpServletResponse response) throws ServletException{
		String courseName = request.getParameter("deleteTarget");
		Connection con = DBHandler.getHandler().getLocalCon();
		Course course;
		try {
			course = DBHandler.getHandler().getCourse(courseName, con);
		} catch (Exception e) {
			finishTransaction(con, false);
			e.printStackTrace();
			throw new ServletException();
		} 
		ArrayList<String> users = course.getUsers();
		
		for(String userName:users){
			try {
				doUnRegisterFromCourse(userName, courseName, con);
			} catch (Exception e) {
				finishTransaction(con, false);
				e.printStackTrace();
				throw new ServletException();
			}
		}
		
		try {
			DBHandler.getHandler().removeCourse(courseName, con);
		} catch (SQLException e) {
			finishTransaction(con, false);
			e.printStackTrace();
			throw new ServletException();
		}
		finishTransaction(con, true);
		DBHandler.getHandler().returnLocalCon(con);
		request.getSession().setAttribute("SuccessMessage", "Course removed successfully");
		RequestDispatcher rd = getServletConfig().getServletContext()
		.getRequestDispatcher("/WEB-INF/JSP-Files/courses.jsp");
		try {
			rd.forward(request, response);
		} catch (IOException e) {
		}
	}
	
	public ResultObject canRegister(String currentUser, String courseName, Connection con) throws ServletException{
		User user;
		ArrayList<Course> currentUserCourses;
		Course course;
		try {
			user = DBHandler.getHandler().getUser(currentUser, con);
			course = DBHandler.getHandler().getCourse(courseName, con);
			currentUserCourses = DBHandler.getHandler().getCoursesByUserName(currentUser, con);
		} catch (Exception e) {
			finishTransaction(con, false);
			e.printStackTrace();
			throw new ServletException();
		} 
		
		if (user.getCourses().contains(courseName)) {
			DBHandler.getHandler().returnLocalCon(con);
			return new ResultObject(false, "AlreadyRegistered");
		}
		
		//checking overlap with already registered courses (2)
		for (Course c : currentUserCourses){
			if (c.checkOverlap(course)){
				DBHandler.getHandler().returnLocalCon(con);
				return new ResultObject(false, "CoursesOverlap");
			}
		}
		
		//checking if already registered to atleast 3 groups (3)
		HashSet<Integer> intSet = new HashSet<Integer>();
		for (Course c:currentUserCourses){
			intSet.add(c.getGroupNum());
		}
		if(intSet.size()+1 > MAXGROUPS){
			DBHandler.getHandler().returnLocalCon(con);
			return new ResultObject(false, "ThreeGroups");
		}
		
		if(course.getNumOfRegistered() + 1 > course.getCapacity()){
			DBHandler.getHandler().returnLocalCon(con);
			return new ResultObject(false, "ExceedingCapacity");
		}
		
		DBHandler.getHandler().returnLocalCon(con);
		return new ResultObject(true, "GoodToGo");
	}
	
	public void doRegisterToCourse(HttpServletRequest request, HttpServletResponse response) throws ServletException{
		Connection con = DBHandler.getHandler().getLocalCon();
		String currentUser = (String)request.getSession().getAttribute("LoginName");
		String courseName = request.getParameter("registerTarget");
		
		ResultObject result;
		try {
			result = canRegister(currentUser, courseName, con);
		} catch (ServletException e) {
			finishTransaction(con, false);
			e.printStackTrace();
			throw new ServletException();
		}
		if (!result.getResult()){
			if (result.getMessage().equals("AlreadyRegistered"))
				forwardOnError(request, response, "CourseRegistrationError", "You are already registered to this course.", "/WEB-INF/JSP-Files/courses.jsp");
			else if (result.getMessage().equals("CoursesOverlap"))
				forwardOnError(request, response, "CourseRegistrationError", "The course sessions overlap with other courses you registered to.", "/WEB-INF/JSP-Files/courses.jsp");
			else if (result.getMessage().equals("ThreeGroups"))
				forwardOnError(request, response, "CourseRegistrationError", "You are already registered to 3 different groups.", "/WEB-INF/JSP-Files/courses.jsp");
			else if (result.getMessage().equals("ExceedingCapacity"))
				forwardOnError(request, response, "CourseRegistrationError", "The course is full.", "/WEB-INF/JSP-Files/courses.jsp");
			finishTransaction(con, false);
			return;
		}
		
		try {
			DBHandler.getHandler().registerUserToCourse(currentUser, courseName, con);
		} catch (Exception e) {
			finishTransaction(con, false);
			e.printStackTrace();
			throw new ServletException();
		}
		finishTransaction(con, true);
		DBHandler.getHandler().returnLocalCon(con);
		request.getSession().setAttribute("SuccessMessage", "Course added successfully");
		RequestDispatcher rd = getServletConfig().getServletContext()
				.getRequestDispatcher("/WEB-INF/JSP-Files/courses.jsp");
		try {
			rd.forward(request, response);
		} catch (IOException e) {
		}
	}
	
	public void doUnRegisterFromCourse(HttpServletRequest request,
			HttpServletResponse response) throws ServletException{
		Connection con = DBHandler.getHandler().getLocalCon();
		String currentUser = (String) request.getSession().getAttribute(
				"LoginName");
		String courseName = request.getParameter("unRegisterTarget");
		try {
			if (!doUnRegisterFromCourse(currentUser, courseName, con)){
				finishTransaction(con, false);
				DBHandler.getHandler().returnLocalCon(con);
				forwardOnError(request, response, "CourseRegistrationError",
						"You are not registered to this course.",
						"/WEB-INF/JSP-Files/courses.jsp");
				return;
			}
		} catch (Exception e) {
			finishTransaction(con, false);
			e.printStackTrace();
			throw new ServletException();
		}

		finishTransaction(con, true);
		DBHandler.getHandler().returnLocalCon(con);
		request.getSession().setAttribute("SuccessMessage",
				"Course removed successfully");
		RequestDispatcher rd = getServletConfig().getServletContext()
				.getRequestDispatcher("/WEB-INF/JSP-Files/courses.jsp");
		try {
			rd.forward(request, response);
		} catch (IOException e) {
		}
	}
	
	public boolean doUnRegisterFromCourse(String userName, String courseName, Connection con) throws ServletException {
		User user;
		try {
			user = DBHandler.getHandler().getUser(userName, con);
		} catch (Exception e) {
			finishTransaction(con, false);
			e.printStackTrace();
			throw new ServletException();
		}
		if (!user.getCourses().contains(courseName)) {
			return false;
		}
		try {
			DBHandler.getHandler()
					.unRegisterUserFromCourse(userName, courseName, con);
		} catch (Exception e) {
			finishTransaction(con, false);
			e.printStackTrace();
			throw new ServletException();
		}
		return true;
	}
	
	public void doPost(HttpServletRequest request, HttpServletResponse response)
		throws ServletException, IOException {
		if (request.getParameter("addCourse") != null){
			doAddNewCourse(request, response);
			return;
		}
	}
}
